package cn.italkcloud.cph.service.impl;

import cn.italkcloud.cph.constants.ReportTimeRangeEnum;
import cn.italkcloud.cph.entity.ReportVosCdr;
import cn.italkcloud.cph.entity.dto.vos.CdrDTO;
import cn.italkcloud.cph.entity.req.GetReportVosCdrReq;
import cn.italkcloud.cph.mapper.ReportVosCdrMapper;
import cn.italkcloud.cph.service.IReportVosCdrService;
import cn.italkcloud.cph.utils.DateUtil;
import cn.italkcloud.cph.utils.SqlUtil;
import cn.italkcloud.cph.utils.StringUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * VOS通话数据报表-自定时间范围内的数据 服务实现类
 * </p>
 *
 * @author dominate
 * @since 2025-04-30
 */
@Service
public class ReportVosCdrServiceImpl extends ServiceImpl<ReportVosCdrMapper, ReportVosCdr> implements IReportVosCdrService {

    private static void mergeReport(ReportVosCdr base, ReportVosCdr target) {
        if (base.getLatestVosCdrId() > target.getLatestVosCdrId()) {
            target.setLatestVosCdrId(base.getLatestVosCdrId());
        }
        if (base.getMaxDuration() > target.getMaxDuration()) {
            target.setMaxDuration(base.getMaxDuration());
        }
        if (base.getMinDuration() < target.getMinDuration()) {
            target.setMinDuration(base.getMinDuration());
        }
        target.setTotalDuration(target.getTotalDuration() + base.getTotalDuration());
        target.setTotalCount(target.getTotalCount() + base.getTotalCount());
        target.setTotalEndCalleeCount(target.getTotalEndCalleeCount() + base.getTotalEndCalleeCount());
        target.setTotalEndCallerCount(target.getTotalEndCallerCount() + base.getTotalEndCallerCount());
        target.setTotalEndServerCount(target.getTotalEndServerCount() + base.getTotalEndServerCount());
        target.setAnswerCount(target.getAnswerCount() + base.getAnswerCount());
        target.setAnswerEndCalleeCount(target.getAnswerEndCalleeCount() + base.getAnswerEndCalleeCount());
        target.setAnswerEndCallerCount(target.getAnswerEndCallerCount() + base.getAnswerEndCallerCount());
        target.setAnswerEndServerCount(target.getAnswerEndServerCount() + base.getAnswerEndServerCount());
    }

    private static String createCdrKey(ReportVosCdr cdr) {
        return cdr.getVosAgentAccount() + StringUtil.STRING_ARRAY_SPLIT
                + cdr.getVosCustomerAccount() + StringUtil.STRING_ARRAY_SPLIT
                + cdr.getCalleeVosGatewayId() + StringUtil.STRING_ARRAY_SPLIT
                + cdr.getCallerVosGatewayId() + StringUtil.STRING_ARRAY_SPLIT;
    }

    private static String createCdrKey(CdrDTO cdr) {
        return cdr.getAgentaccount() + StringUtil.STRING_ARRAY_SPLIT
                + cdr.getCustomeraccount() + StringUtil.STRING_ARRAY_SPLIT
                + cdr.getCallergatewayid() + StringUtil.STRING_ARRAY_SPLIT
                + cdr.getCalleegatewayid() + StringUtil.STRING_ARRAY_SPLIT;
    }

    private static List<ReportVosCdr> analysis(long intervalTimeMs, List<CdrDTO> cdrList) {
        long latestTime = parseStartIntervalMs(cdrList.get(0).getStarttime(), intervalTimeMs);
        List<CdrDTO> rangeCdrList = new ArrayList<>();
        List<ReportVosCdr> reportList = new ArrayList<>();
        // 按时间间隔分组
        for (CdrDTO cdr : cdrList) {
            if (cdr.getStarttime() - latestTime > intervalTimeMs) {
                // 超过设定的时间间隔，把之前的一组数据合并生成一条报表数据
                reportList.add(mergeCdrListToReport(intervalTimeMs, rangeCdrList));
                // 清理数据组
                rangeCdrList.clear();
            }
            rangeCdrList.add(cdr);
            latestTime = cdr.getStarttime();
        }
        if (CollectionUtils.isNotEmpty(rangeCdrList)) {
            reportList.add(mergeCdrListToReport(intervalTimeMs, rangeCdrList));
        }
        return reportList;
    }

    public static long parseStartIntervalMs(long timestamp, long intervalMs) {
        long remainder = timestamp % intervalMs;
        return remainder == 0 ? timestamp : timestamp - remainder;
    }

    private static ReportVosCdr mergeCdrListToReport(long intervalMs, List<CdrDTO> rangeCdrList) {
        CdrDTO lastCdr = rangeCdrList.get(rangeCdrList.size() - 1);
        ReportVosCdr report = new ReportVosCdr(lastCdr);
        long startMs = parseStartIntervalMs(lastCdr.getStarttime(), intervalMs);
        report.setEndTime(new Date(startMs + intervalMs));
        report.setStartTime(new Date(startMs));
        for (CdrDTO cdr : rangeCdrList) {
            report.setTotalCount(report.getTotalCount() + 1);
            boolean isAnswer = cdr.getFeetime() > 0;
            if (isAnswer) {
                report.setAnswerCount(report.getAnswerCount() + 1);
                if (report.getMinDuration() < 0 || report.getMinDuration() > cdr.getHoldtime()) {
                    report.setMinDuration(cdr.getHoldtime());
                }
                if (report.getMaxDuration() < cdr.getHoldtime()) {
                    report.setMaxDuration(cdr.getHoldtime());
                }
                report.setTotalDuration(report.getTotalDuration() + cdr.getHoldtime());
            }
            switch (cdr.getEnddirection()) {
                case 0:
                    // 主叫挂断
                    report.setTotalEndCallerCount(report.getTotalEndCallerCount() + 1);
                    if (isAnswer) {
                        report.setAnswerEndCallerCount(report.getAnswerEndCallerCount() + 1);
                    }
                    break;
                case 1:
                    // 被叫挂断
                    report.setTotalEndCalleeCount(report.getTotalEndCalleeCount() + 1);
                    if (isAnswer) {
                        report.setAnswerEndCalleeCount(report.getAnswerEndCalleeCount() + 1);
                    }
                    break;
                case 2:
                    // 服务器挂断
                    report.setTotalEndServerCount(report.getTotalEndServerCount() + 1);
                    if (isAnswer) {
                        report.setAnswerEndServerCount(report.getAnswerEndServerCount() + 1);
                    }
                    break;
            }
        }
        return report;
    }

    private static Map<String, List<CdrDTO>> makeCdrGroup(List<CdrDTO> cdrList) {
        // 确保按时间排序
        List<CdrDTO> ascList = cdrList.stream()
                .sorted(Comparator.comparingLong(CdrDTO::getStarttime)).collect(Collectors.toList());
        // 按报表字段分组
        Map<String, List<CdrDTO>> cdrGroupMap = new HashMap<>();
        for (CdrDTO cdr : ascList) {
            String key = createCdrKey(cdr);
            if (!cdrGroupMap.containsKey(key)) {
                cdrGroupMap.put(key, new ArrayList<>());
            }
            cdrGroupMap.get(key).add(cdr);
        }
        return cdrGroupMap;
    }

    @Override
    public Map<String, List<ReportVosCdr>> analysis(ReportTimeRangeEnum reportTimeRange, List<CdrDTO> cdrList) {
        // 按报表字段分组
        Map<String, List<CdrDTO>> cdrGroupMap = makeCdrGroup(cdrList);
        long intervalTimeMs = reportTimeRange.getSecond() * 1000;
        Map<String, List<ReportVosCdr>> reportCdrMap = new HashMap<>();
        for (Map.Entry<String, List<CdrDTO>> reportMapEntry : cdrGroupMap.entrySet()) {
            reportCdrMap.put(reportMapEntry.getKey(), analysis(intervalTimeMs, reportMapEntry.getValue()));
        }
        return reportCdrMap;
    }

    @Override
    @Transactional
    public List<ReportVosCdr> analysisAfterSave(int serverId, ReportTimeRangeEnum reportTimeRange, List<CdrDTO> cdrList) {
        // 按报表字段分组
        Map<String, List<CdrDTO>> cdrGroupMap = makeCdrGroup(cdrList);
        long intervalTimeMs = reportTimeRange.getSecond() * 1000;
        List<ReportVosCdr> reportList = new ArrayList<>();
        for (List<CdrDTO> reportCdrList : cdrGroupMap.values()) {
            reportList.addAll(analysis(intervalTimeMs, reportCdrList));
        }
        List<ReportVosCdr> ascReportList = reportList.stream()
                .sorted(Comparator.comparingLong(ReportVosCdr::getLatestVosCdrId))
                .peek(cdr -> cdr.setServerId(serverId))
                .collect(Collectors.toList());
        saveBatch(ascReportList);
        return ascReportList;
    }

    @Override
    @Transactional
    public void mergeReport(int serverId, Date startDate, Date endDate) {
        QueryWrapper<ReportVosCdr> query = new QueryWrapper<>();
        query.lambda().eq(ReportVosCdr::getServerId, serverId)
                .between(ReportVosCdr::getStartTime, startDate, endDate);
        List<ReportVosCdr> list = list(query);
        if (CollectionUtils.isEmpty(list)) {
            return;
        }
        // 确保按时间排序
        List<ReportVosCdr> ascList = list.stream()
                .sorted(Comparator.comparing(ReportVosCdr::getStartTime))
                .collect(Collectors.toList());
        // 按账号网关进行分组
        Map<String, List<ReportVosCdr>> reportMap = new HashMap<>();
        for (ReportVosCdr cdr : ascList) {
            String key = createCdrKey(cdr);
            if (!reportMap.containsKey(key)) {
                reportMap.put(key, new ArrayList<>());
            }
            reportMap.get(key).add(cdr);
        }
        List<ReportVosCdr> mergeCdrList = new ArrayList<>();
        List<Integer> delCdrIdList = new ArrayList<>();
        for (List<ReportVosCdr> value : reportMap.values()) {
            ReportVosCdr latestCdr = null;
            for (ReportVosCdr cdr : value) {
                if (latestCdr == null) {
                    latestCdr = cdr;
                    continue;
                }
                if (latestCdr.getStartTime() == cdr.getStartTime()) {
                    // 同一时间段，删除该条，数据合并到上一条
                    delCdrIdList.add(cdr.getId());
                    mergeReport(cdr, latestCdr);
                    mergeCdrList.add(latestCdr);
                    continue;
                }
                latestCdr = cdr;
            }
        }
        if (mergeCdrList.isEmpty()) {
            return;
        }
        if (updateBatchById(mergeCdrList)) {
            removeByIds(delCdrIdList);
        }
    }

    @Override
    public int getLatestVosCdrId(int serverId, Date day) {
        QueryWrapper<ReportVosCdr> query = new QueryWrapper<>();
        query.lambda().eq(ReportVosCdr::getServerId, serverId)
                .between(ReportVosCdr::getStartTime, DateUtil.getDateBegin(day), DateUtil.getDateEnd(day))
                .orderByDesc(ReportVosCdr::getLatestVosCdrId)
                .select(ReportVosCdr::getLatestVosCdrId)
                .last(SqlUtil.limitOne());
        List<ReportVosCdr> list = list(query);
        if (CollectionUtils.isEmpty(list)) {
            return 0;
        }
        return list.get(0).getLatestVosCdrId();
    }

    @Override
    public List<ReportVosCdr> getReport(GetReportVosCdrReq req) {
        QueryWrapper<ReportVosCdr> query = new QueryWrapper<>();
        query.lambda().eq(ReportVosCdr::getServerId, req.getServerId())
                .gt(Objects.nonNull(req.getStartTime()), ReportVosCdr::getStartTime, new Date(req.getStartTime()))
                .lt(Objects.nonNull(req.getEndTime()), ReportVosCdr::getStartTime, new Date(req.getEndTime()))
                .eq(Objects.nonNull(req.getAgentAccount()), ReportVosCdr::getVosAgentAccount, req.getAgentAccount())
                .eq(Objects.nonNull(req.getCustomerAccount()), ReportVosCdr::getVosCustomerAccount, req.getCustomerAccount())
                .eq(Objects.nonNull(req.getCalleeGatewayId()), ReportVosCdr::getCalleeVosGatewayId, req.getCalleeGatewayId())
                .eq(Objects.nonNull(req.getCallerGatewayId()), ReportVosCdr::getCallerVosGatewayId, req.getCallerGatewayId())
                .orderByAsc(ReportVosCdr::getStartTime);
        return list(query);
    }
}
